package com.qclix.inv.webui;

import com.qclix.inv.*;
import com.qclix.inv.pdf.InvoicePdfGenerationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@Controller
@RequestMapping(value = "/timesheet")
public class TimesheetController {
    private static Logger logger = LoggerFactory.getLogger(TimesheetController.class);

    @Autowired
    private InvoiceService invoiceService;
    @Autowired
    private InvoicePdfGenerationService pdfService;

    public InvoiceService getInvoiceService() {
        return invoiceService;
    }

    public void setInvoiceService(InvoiceService invoiceService) {
        this.invoiceService = invoiceService;
    }

    private int getMonthNumberFromParameter(String month) {
        int monthNumber = -1;
        if (month != null) {
            try {
                monthNumber = Integer.parseInt(month) - 1;
                if (monthNumber < 0 || monthNumber > 11) { //month must be between 1 and 12 for readability
                    logger.warn("The month " + month + " is not between 1 and 12");
                }
            } catch (NumberFormatException e) {
                logger.warn("The month " + month + " is not a valid integer");
            }
        } else {
            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            monthNumber = cal.get(Calendar.MONTH);
        }
        return monthNumber;
    }

    private String getMonthPageValue(int value) {
        if (value < 9) {
            return "0" + (value + 1);
        }
        return "" + (value + 1);
    }

    private int getYearNumberFromParameter(String year) {
        int yearNumber = -1;
        if (year != null) {
            try {
                yearNumber = Integer.parseInt(year);
            } catch (NumberFormatException e) {
                logger.warn("The year " + year + " is not a valid year");
            }
        } else {
            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            yearNumber = cal.get(Calendar.YEAR);
        }
        return yearNumber;
    }

    @RequestMapping(value = "new-timesheet")
    public ModelAndView newTimesheetPage(@RequestParam(required = false) String month, @RequestParam(required = false) String year) {
        int monthNumber = getMonthNumberFromParameter(month);
        int yearNumber = getYearNumberFromParameter(year);

        ModelAndView mav = new ModelAndView("new-timesheet");

        Timesheet tempTimesheet = null;
        try {
            tempTimesheet = invoiceService.getTimesheet(monthNumber, yearNumber);
        } catch (Throwable e) {
            logger.error("Could not retrieve timesheet for " + (monthNumber + 1) + "/" + yearNumber, e);
        }
        if (tempTimesheet != null) {
            TimesheetHelper.addNonPartOfInvoiceDays(tempTimesheet);
        } else {
            tempTimesheet = TimesheetHelper.buildTimesheet(monthNumber, yearNumber);
        }
        
        try {
            TimesheetHelper.setHolidays(tempTimesheet, invoiceService.getHolidays());
        } catch (Throwable e) {
            logger.error("Could not set holidays for timesheet of " + (monthNumber + 1) + "/" + yearNumber, e);
        }

        mav.addObject("timesheet", tempTimesheet);
        mav.addObject("startDate", CalendarHelper.getFirstDayOfMonth(monthNumber, yearNumber));
        mav.addObject("availableMonths", getAvailableMonthAndYearsForNewTimesheet());
        mav.addObject("selectedMonth", getMonthPageValue(monthNumber));
        mav.addObject("selectedYear", "" + yearNumber);
        return mav;
    }

    private Calendar getFirstDayOfMonth(int monthNumber, int yearNumber) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, 1);
        cal.set(Calendar.MONTH, monthNumber);
        cal.set(Calendar.YEAR, yearNumber);
        return cal;
    }

    @RequestMapping(value = "view-timesheet")
    public ModelAndView viewTimesheetPage(@RequestParam(required = false) String month, @RequestParam(required = false) String year) {
        int monthNumber = getMonthNumberFromParameter(month);
        int yearNumber = getYearNumberFromParameter(year);

        ModelAndView mav;
        Timesheet timesheet = null;
        try {
            timesheet = invoiceService.getTimesheet(monthNumber, yearNumber);
        } catch (Throwable e) {
            logger.error("Could not retrieve timesheet for " + (monthNumber + 1) + "/" + yearNumber, e);
        }

        if (timesheet == null) {
            mav = new ModelAndView("new-timesheet");
            timesheet = TimesheetHelper.buildTimesheet(monthNumber, yearNumber);
            mav.addObject("timesheet", timesheet);
        } else {
            mav = new ModelAndView("view-timesheet");
            TimesheetHelper.addNonPartOfInvoiceDays(timesheet);
        }

        try {
            TimesheetHelper.setHolidays(timesheet, invoiceService.getHolidays());
        } catch (Throwable e) {
            logger.error("Could not set holidays for timesheet of " + (monthNumber + 1) + "/" + yearNumber, e);
        }

        mav.addObject("timesheet", timesheet);
        mav.addObject("startDate", CalendarHelper.getFirstDayOfMonth(monthNumber, yearNumber));
        mav.addObject("endDate", CalendarHelper.getLastDayOfMonth(monthNumber, yearNumber));
        mav.addObject("availableMonths", getExistingTimesheetMonthAndYears());
        mav.addObject("selectedMonth", getMonthPageValue(monthNumber));
        mav.addObject("selectedYear", "" + yearNumber);
        return mav;
    }


    @RequestMapping(value = "save-timesheet")
    public
    @ResponseBody
    Map<String, ? extends Object> saveTimesheet(@RequestBody Timesheet timesheet) {
        Map<String, String> returnMap = new HashMap<String, String>();
        try {
            invoiceService.saveTimesheet(timesheet);
        } catch (Throwable e) {
            logger.error("Could not store new timesheet", e);
            return returnMap;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(timesheet.getDays().iterator().next().getDate());
        returnMap.put("month", "" + (cal.get(Calendar.MONTH) + 1));
        returnMap.put("year", "" + cal.get(Calendar.YEAR));
        return returnMap;
    }

    @RequestMapping(value = "download.pdf")
    public ModelAndView getPdf(HttpServletRequest request, HttpServletResponse response) throws Exception {
        int monthNumber = getMonthNumberFromParameter(request.getParameter("month"));
        int yearNumber = getYearNumberFromParameter(request.getParameter("year"));

        response.setContentType("application-pdf");
        try {
            Timesheet timesheet = invoiceService.getTimesheet(monthNumber, yearNumber);
            TimesheetHelper.addNonPartOfInvoiceDays(timesheet);

            try {
                TimesheetHelper.setHolidays(timesheet, invoiceService.getHolidays());
            } catch (Throwable e) {
                logger.error("Could not set holidays for timesheet of " + (monthNumber + 1) + "/" + yearNumber, e);
            }
            response.getOutputStream().write(pdfService.getPDF(timesheet));
            response.getOutputStream().close();
        } catch (Throwable e) {
            logger.error("Could not retrieve PDF for timesheet of " + (monthNumber + 1) + "/" + yearNumber, e);
            return null;
        }
        return null;
    }

    private SortedSet<Calendar> getAvailableMonthAndYearsForNewTimesheet() {
        SortedSet<Calendar> firstDaysOfMonth = new TreeSet<Calendar>();
        Calendar firstDayOfFirstMonth = Calendar.getInstance();
        firstDayOfFirstMonth.add(Calendar.YEAR, -1);
        Calendar firstDayOfLastMonth = Calendar.getInstance();
        firstDayOfLastMonth.add(Calendar.MONTH, 3);
        Calendar tempCal = (Calendar) firstDayOfFirstMonth.clone();
        do {
            firstDaysOfMonth.add((Calendar) tempCal.clone());
            tempCal.add(Calendar.MONTH, 1);
        } while (! tempCal.after(firstDayOfLastMonth));
        return firstDaysOfMonth;
    }

    private SortedSet<Calendar> getExistingTimesheetMonthAndYears() {
        try {
            return invoiceService.getTimesheetFirstDays();
        } catch (Throwable e) {
            logger.error("Could not retrieve the existing timesheet first days", e);
            return null;
        }
    }
}
